A quick intro to the intro to R Lesson Series


This ‘Intro to R Lesson Series’ is brought to you by the Centre for the Analysis of Genome Evolution & Function’s (CAGEF) bioinformatics training initiative. This course was developed based on feedback on the needs and interests of the Department of Cell & Systems Biology and the Department of Ecology and Evolutionary Biology.

This lesson is the last in a 6-part series. Hopefully you have learned a bit about how to import and manipulate data, make exploratory plots, perform some basic statistical tests, test a regression model, and make some even prettier plots and documents to share to results.


Today we are going to learn to write functions, which can save you time and help scale up your analyses. It will also help to minimize copy and pasting, redundancy, and feeling like a newb.


The structure of the class is a code-along style. It is hands on. The lecture AND code we are going through are available on GitHub for download at https://github.com/eacton/CAGEF (Note: repo is private until approved), so you can spend the time coding and not taking notes. As we go along, there will be some challenge questions and multiple choice questions on Socrative. At the end of the class if you could please fill out a post-lesson survey (https://www.surveymonkey.com/r/X5C2JGR), it will help me further develop this course and would be greatly appreciated.


Packages Used in This Lesson

The following packages are used in this lesson:

tidyverse (ggplot2, tidyr, dplyr)
testthat assertr

Please install and load these packages for the lesson. In this document I will load each package separately, but I will not be reminding you to install the package. Remember: these packages may be from CRAN OR Bioconductor.


Highlighting

grey background - a package, function, code or command
italics - an important term or concept
bold - heading or a term that is being defined
blue text - named or unnamed hyperlink


Objective: At the end of this session you will be able to write functions, making your coding more efficient, understandable, reproducible, and hopefully less frustrating.

Load the packages!

library(tidyverse)
library(testthat)
library(assertr)

Our Dataset

Metagenomic 16SrRNA sequencing of latrines from Tanzania and Vietnam at different depths (multiples of 20cm).

We have 2 csv files (change one to tsv or xlsx - maybe both and make an additional files and get a google spreadsheet): 1. A metadata file (Naming conventions: [Country_LatrineNo_Depth]) with sample names and environmental variables. 2. A table of species abundance.

B Torondel, JHJ Ensink, O Gunvirusdu, UZ Ijaz, J Parkhill, F Abdelahi, V-A Nguyen, S Sudgen, W Gibson, AW Walker, and C Quince. Assessment of the influence of intrinsic environmental and geographical factors on the bacterial ecology of pit latrines Microbial Biotechnology, 9(2):209-223, 2016. DOI:10.1111/1751-7915.12334


Why Functions?

I want to know the median and median absolute deviation of OTUs in all of our samples for Clostrida.

Great. What about the median and median absolute deviation for Bacilli? Hmmmm. I guess I’ll just copy and paste the above code because it will take 2 seconds.

Maybe I’ll check out Bacteroidia, too… by copy and pasting AGAIN.

Come to think of it, there are a lot of zeros in this data. Maybe I should be looking at mean and standard deviation instead of median and mad. I’ll just go back and change this in each of my 3 lines of code.

dat %>% filter(Taxa == "Clostridia") %>% summarize(mean = mean(OTUs), sd = sd(OTUs))
dat %>% filter(Taxa == "Bacilli") %>% summarize(mean = mean(OTUs), sd = sd(OTUs))
dat %>% filter(Taxa == "Bacteroidia") %>% summarize(mean = mean(OTUs), sd = sd(OTUs))

Does any of this feel familiar??

Functions allow us to:

The Structure of a Function

A function has 3 parts:

We can look at these for a function we are familiar with, read.csv. Each argument is specified with $ and any default values are listed [1]. To use read.csv you must input a file - this argument has no default value. The rest of the arguments have a default value (such as header = TRUE), but can be modified by specifying the argument (by inputting header = FALSE). Most function defaults as ‘sensible’, but if you have not used the function before it is worth reading about the arguments in the help menu. ESPECIALLY do this with mathematical functions to see what assumptions you are making by using the default parameters. read.csv also has the magical ... argument. This argument allows you to pass arbitrary arguments to another function. We will see this in use shortly.

formals(read.csv)
$file


$header
[1] TRUE

$sep
[1] ","

$quote
[1] "\""

$dec
[1] "."

$fill
[1] TRUE

$comment.char
[1] ""

$...

The body of read.csv looks suspiciously like the function read.table because it is, indeed a ‘wrapper’ function which uses an existing function with different values for default arguments.

body(read.csv)
read.table(file = file, header = header, sep = sep, quote = quote, 
    dec = dec, fill = fill, comment.char = comment.char, ...)

The environment for read.csv is the utils package. When we make a function, it will be created in ‘.GlobalEnv’ the ‘global environment’. The global environment is our interactive workspace, viewed in the environment window in the top right, and can be thought of as a bag that holds all of our variables and functions. Note that we can use the dropdown menu to change the environment to package:utils, and we can find read.csv in that environment.

environment(read.csv)
<environment: namespace:utils>

Writing Functions

The basic form of a function takes in arguments (specifications by the user), has statements or expressions (what the function does), and returns an object. A function does not need to be named to be used, however, the greatest utility comes from reusing functions. A named function is called using parentheses: named_function(arguments).

Different from other programming languages, R is not picky about whitespace (the arrangement of tabs, spaces, indents). However formatting your function as below (statements indented below your arguments) will help to make it readable and familiar to other languages.

This may be a good time to point out that placing your cursor beside a closing bracket will highlight its corresponding opening bracket. This is really useful as your code gets more complex. Also, try deleting the closing bracket beside ‘object’. You will see that R is giving you a series of warnings (brackets highlighted in red, and ‘x’ beside 3 lines of code). Again, this is a helpful warning that saves you time from running an incomplete function.

named_function <- 
  function(arguments) {
      statments
      return(object) 
  }

We want to write a function that will calculate the mean and standard deviation for a given Taxa.

We can start by naming our function something vaguely informative. (Technically, you can name your functions and variables whatever you want. I could name them after Avengers characters, but people (or myself in 3 months) would have to work pretty hard to figure out what I was doing. Remember, with great power comes great responsibility).

Our function is going to take ‘Taxa’ as an argument. For some of you, when you type ‘{’ R will automatically give you your closing bracket ‘}’ so you won’t forget - ever trying to be helpful, thanks R. Next we can take our previous code, and save the output into an object called ‘summary_dat’. This is because I don’t want to overwrite my original data frame every time I run the function. We replace our specific Taxa, ‘Clostridia’, with our argument, ‘Taxa’. This is our expression. All we have to do now is return our data frame and make sure all of our brackets match.

Voila!

When you run this function, it will appear, named and with its arguments, in the global environment. If you click on the white box to the right of the function, you can see the code for your function. You can now use your function.

out <- mean_func("Bacteriodia")
[1] "This is the mean and sd for Bacteriodia"

Understanding Lexical Scoping

-may want to be able to take into different data frames…

Allows us to vary the Taxa and the functions.

mean_func("Clostridia", funs = list(mean = mean, median = median, sd = mean))
$mean
[1] 3423.407

$median
[1] 1432

$sd
[1] 3423.407

Defensive Programming

Testing Arguments and Validity of your Function

You might think that you are done with this function. You have the answers, right? But functions are not foolproof.

mean_func("Clostridia", funs = list(mean = quantile, median = median, sd = mean))
$mean
   0%   25%   50%   75%  100% 
    0   496  1432  4761 17572 

$median
[1] 1432

$sd
[1] 3423.407

Hmmm… mislabelled numbers could get a little confusing.

What if I wanted the summary statistics for 3 Taxa at once?

Uh oh. That’s bad. I didn’t want to merge the 3 Taxa… or did it take the last value?

Your next step is to try and intentionally break your function. Give it input that wasn’t meant for it and see what happens. Trouble-shooting this way is called ‘defensive programming’. What we are going to do is implement checks and balances such that our function can’t do anything weird.

Initial checks can be things like - what happens when I give an input that is numeric when I am expecting character data? What happens if there is an NA in the dataset? What happens if there is a typo? What happens if I am given an unexpected data structure? What happens if there is more or less data than I am expecting? What happens when I input logical data?

mean_func()

Error Handling

Unit Tests

-testthat assumption for function name = name?

Returns the last line… unless explicitly called with return

generalize to country, bacteria, #otus above/below mean? ranking position?

Basic Syntax of Functions in R

  • the structure of a function (formals, body, parent environment)
  • understanding scoping
  • naming conventions
  • generalizing a function
  • setting default values
  • adding ‘…’
  • the output of your function
  • sourcing functions
  • NAs in functions
  • return

Handling Errors

  • stopifnot(), warning(), suppressWarnings(), tryCatch()
  • if stop

Testing Arguments and Validity of your Function

  • informal testing
  • testthat() for formal unit testing
  • assertthat

Upscaling with functions

functions can be:

  • assigned in variables
  • stored in lists
  • passed as arguments to other functions
  • created inside other functions
  • output by other functions

DRY principle - Don’t Repeat Yourself


Challenge

Write a function to check that the packages c(“dplyr”, “readxml”, “tidyr”) are installed and to load the packages. The function should install the packages if they are not already installled. Write a warning if the package is not installed, but is being installed. Will this function work for other packages?






Challenge

Write a function to read all .csv files in a directory into R and save each of them in a separate data frame.





Resources:

http://stat545.com/block011_write-your-own-function-01.html
http://stat545.com/block011_write-your-own-function-02.html
http://stat545.com/block011_write-your-own-function-03.html
http://mazamascience.com/WorkingWithData/?p=912 http://adv-r.had.co.nz/Functions.html

Post-Lesson Assessment


Your feedback is essential to help the next cohort of trainees. Please take a minute to complete the following short survey: https://www.surveymonkey.com/r/X5C2JGR




Thanks for coming!!!

LS0tCnRpdGxlOiAiTGVzc29uIDYgLSBTY2FsaW5nIHVwIHlvdXIgQW5hbHlzZXM6IFdyaXRpbmcgRnVuY3Rpb25zIGluIFIiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgICAgICAgIGtlZXBfbWQ6IHllcwogICAgICAgICAgdG9jOiBUUlVFCiAgICAgICAgICB0b2NfZGVwdGg6IDMKICBodG1sX25vdGVib29rOgogICAgICAgICAgdG9jOiBUUlVFCiAgICAgICAgICB0b2NfZGVwdGg6IDMKLS0tCioqKgoKIVtdKGltZy9jYXJ0b29uMzcucG5nKQoKCjwvYnI+CgojI0EgcXVpY2sgaW50cm8gdG8gdGhlIGludHJvIHRvIFIgTGVzc29uIFNlcmllcwoKPC9icj4KClRoaXMgJ0ludHJvIHRvIFIgTGVzc29uIFNlcmllcycgaXMgYnJvdWdodCB0byB5b3UgYnkgdGhlIENlbnRyZSBmb3IgdGhlIEFuYWx5c2lzIG9mIEdlbm9tZSBFdm9sdXRpb24gJiBGdW5jdGlvbidzIChDQUdFRikgYmlvaW5mb3JtYXRpY3MgdHJhaW5pbmcgaW5pdGlhdGl2ZS4gVGhpcyBjb3Vyc2Ugd2FzIGRldmVsb3BlZCBiYXNlZCBvbiBmZWVkYmFjayBvbiB0aGUgbmVlZHMgYW5kIGludGVyZXN0cyBvZiB0aGUgRGVwYXJ0bWVudCBvZiBDZWxsICYgU3lzdGVtcyBCaW9sb2d5IGFuZCB0aGUgRGVwYXJ0bWVudCBvZiBFY29sb2d5IGFuZCBFdm9sdXRpb25hcnkgQmlvbG9neS4gCgoKVGhpcyBsZXNzb24gaXMgdGhlIGxhc3QgaW4gYSA2LXBhcnQgc2VyaWVzLiBIb3BlZnVsbHkgeW91IGhhdmUgbGVhcm5lZCBhIGJpdCBhYm91dCBob3cgdG8gaW1wb3J0IGFuZCBtYW5pcHVsYXRlIGRhdGEsIG1ha2UgZXhwbG9yYXRvcnkgcGxvdHMsIHBlcmZvcm0gc29tZSBiYXNpYyBzdGF0aXN0aWNhbCB0ZXN0cywgdGVzdCBhIHJlZ3Jlc3Npb24gbW9kZWwsIGFuZCBtYWtlIHNvbWUgZXZlbiBwcmV0dGllciBwbG90cyBhbmQgZG9jdW1lbnRzIHRvIHNoYXJlIHRvIHJlc3VsdHMuIAoKCiFbXShpbWcvZGF0YS1zY2llbmNlLWV4cGxvcmUucG5nKQoKPC9icj4KClRvZGF5IHdlIGFyZSBnb2luZyB0byBsZWFybiB0byB3cml0ZSBmdW5jdGlvbnMsIHdoaWNoIGNhbiBzYXZlIHlvdSB0aW1lIGFuZCBoZWxwIHNjYWxlIHVwIHlvdXIgYW5hbHlzZXMuIEl0IHdpbGwgYWxzbyBoZWxwIHRvIG1pbmltaXplIGNvcHkgYW5kIHBhc3RpbmcsIHJlZHVuZGFuY3ksIGFuZCBmZWVsaW5nIGxpa2UgYSBuZXdiLgoKCiFbXShpbWcvc3BvdGlmeS1ob3d0b2J1aWxkbXZwLmdpZikKCjwvYnI+CgpUaGUgc3RydWN0dXJlIG9mIHRoZSBjbGFzcyBpcyBhIGNvZGUtYWxvbmcgc3R5bGUuIEl0IGlzIGhhbmRzIG9uLiBUaGUgbGVjdHVyZSBBTkQgY29kZSB3ZSBhcmUgZ29pbmcgdGhyb3VnaCBhcmUgYXZhaWxhYmxlIG9uIEdpdEh1YiBmb3IgZG93bmxvYWQgYXQgaHR0cHM6Ly9naXRodWIuY29tL2VhY3Rvbi9DQUdFRiBfXyhOb3RlOiByZXBvIGlzIHByaXZhdGUgdW50aWwgYXBwcm92ZWQpX18sIHNvIHlvdSBjYW4gc3BlbmQgdGhlIHRpbWUgY29kaW5nIGFuZCBub3QgdGFraW5nIG5vdGVzLiBBcyB3ZSBnbyBhbG9uZywgdGhlcmUgd2lsbCBiZSBzb21lIGNoYWxsZW5nZSBxdWVzdGlvbnMgYW5kIG11bHRpcGxlIGNob2ljZSBxdWVzdGlvbnMgb24gU29jcmF0aXZlLiBBdCB0aGUgZW5kIG9mIHRoZSBjbGFzcyBpZiB5b3UgY291bGQgcGxlYXNlIGZpbGwgb3V0IGEgcG9zdC1sZXNzb24gc3VydmV5IChodHRwczovL3d3dy5zdXJ2ZXltb25rZXkuY29tL3IvWDVDMkpHUiksIGl0IHdpbGwgaGVscCBtZSBmdXJ0aGVyIGRldmVsb3AgdGhpcyBjb3Vyc2UgYW5kIHdvdWxkIGJlIGdyZWF0bHkgYXBwcmVjaWF0ZWQuIAoKKioqCgojIyMjUGFja2FnZXMgVXNlZCBpbiBUaGlzIExlc3NvbgoKVGhlIGZvbGxvd2luZyBwYWNrYWdlcyBhcmUgdXNlZCBpbiB0aGlzIGxlc3NvbjoKCmB0aWR5dmVyc2VgIChgZ2dwbG90MmAsIGB0aWR5cmAsIGBkcGx5cmApICAgICAKYHRlc3R0aGF0YApgYXNzZXJ0cmAKClBsZWFzZSBpbnN0YWxsIGFuZCBsb2FkIHRoZXNlIHBhY2thZ2VzIGZvciB0aGUgbGVzc29uLiBJbiB0aGlzIGRvY3VtZW50IEkgd2lsbCBsb2FkIGVhY2ggcGFja2FnZSBzZXBhcmF0ZWx5LCBidXQgSSB3aWxsIG5vdCBiZSByZW1pbmRpbmcgeW91IHRvIGluc3RhbGwgdGhlIHBhY2thZ2UuIFJlbWVtYmVyOiB0aGVzZSBwYWNrYWdlcyBtYXkgYmUgZnJvbSBDUkFOIE9SIEJpb2NvbmR1Y3Rvci4gCgoKKioqCiMjIyNIaWdobGlnaHRpbmcKCmBncmV5IGJhY2tncm91bmRgIC0gYSBwYWNrYWdlLCBmdW5jdGlvbiwgY29kZSBvciBjb21tYW5kICAgICAgCippdGFsaWNzKiAtIGFuIGltcG9ydGFudCB0ZXJtIG9yIGNvbmNlcHQgICAgIAoqKmJvbGQqKiAtIGhlYWRpbmcgb3IgYSB0ZXJtIHRoYXQgaXMgYmVpbmcgZGVmaW5lZCAgICAgIAo8c3BhbiBzdHlsZT0iY29sb3I6Ymx1ZSI+Ymx1ZSB0ZXh0PC9zcGFuPiAtIG5hbWVkIG9yIHVubmFtZWQgaHlwZXJsaW5rICAgICAKCgoqKioKX19PYmplY3RpdmU6X18gQXQgdGhlIGVuZCBvZiB0aGlzIHNlc3Npb24geW91IHdpbGwgYmUgYWJsZSB0byB3cml0ZSBmdW5jdGlvbnMsIG1ha2luZyB5b3VyIGNvZGluZyBtb3JlIGVmZmljaWVudCwgdW5kZXJzdGFuZGFibGUsIHJlcHJvZHVjaWJsZSwgYW5kIGhvcGVmdWxseSBsZXNzIGZydXN0cmF0aW5nLgoKCkxvYWQgdGhlIHBhY2thZ2VzIQoKYGBge3Igd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHRlc3R0aGF0KQpsaWJyYXJ5KGFzc2VydHIpCmBgYAoKIyMjT3VyIERhdGFzZXQKCk1ldGFnZW5vbWljIDE2U3JSTkEgc2VxdWVuY2luZyBvZiBsYXRyaW5lcyBmcm9tIFRhbnphbmlhIGFuZCBWaWV0bmFtIGF0IGRpZmZlcmVudCBkZXB0aHMgKG11bHRpcGxlcyBvZiAyMGNtKS4gCgpXZSBoYXZlIDIgY3N2IGZpbGVzIChjaGFuZ2Ugb25lIHRvIHRzdiBvciB4bHN4IC0gbWF5YmUgYm90aCBhbmQgbWFrZSBhbiBhZGRpdGlvbmFsIGZpbGVzIGFuZCBnZXQgYSBnb29nbGUgc3ByZWFkc2hlZXQpOiAKMS4gQSBtZXRhZGF0YSBmaWxlIChOYW1pbmcgY29udmVudGlvbnM6IFtDb3VudHJ5X0xhdHJpbmVOb19EZXB0aF0pIHdpdGggc2FtcGxlIG5hbWVzIGFuZCBlbnZpcm9ubWVudGFsIHZhcmlhYmxlcy4KMi4gQSB0YWJsZSBvZiBzcGVjaWVzIGFidW5kYW5jZS4KCkIgVG9yb25kZWwsIEpISiBFbnNpbmssIE8gR3VudmlydXNkdSwgVVogSWpheiwgSiBQYXJraGlsbCwgRiBBYmRlbGFoaSwgVi1BIE5ndXllbiwgUyBTdWRnZW4sIFcgR2lic29uLCBBVyBXYWxrZXIsIGFuZCBDIFF1aW5jZS4KQXNzZXNzbWVudCBvZiB0aGUgaW5mbHVlbmNlIG9mIGludHJpbnNpYyBlbnZpcm9ubWVudGFsIGFuZCBnZW9ncmFwaGljYWwgZmFjdG9ycyBvbiB0aGUgYmFjdGVyaWFsIGVjb2xvZ3kgb2YgcGl0IGxhdHJpbmVzCk1pY3JvYmlhbCBCaW90ZWNobm9sb2d5LCA5KDIpOjIwOS0yMjMsIDIwMTYuIERPSToxMC4xMTExLzE3NTEtNzkxNS4xMjMzNAoKKioqCgoKIyNXaHkgRnVuY3Rpb25zPwoKCgpgYGB7cn0KZGF0IDwtIHJlYWQuY3N2KCJkYXRhL2xvbmdfU1BFX3BpdGxhdHJpbmUuY3N2IiwgaGVhZGVyID0gVCwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgpgYGAKCkkgd2FudCB0byBrbm93IHRoZSBtZWRpYW4gYW5kIG1lZGlhbiBhYnNvbHV0ZSBkZXZpYXRpb24gb2YgT1RVcyBpbiBhbGwgb2Ygb3VyIHNhbXBsZXMgZm9yIENsb3N0cmlkYS4KCmBgYHtyfQpkYXQgJT4lIGZpbHRlcihUYXhhID09ICJDbG9zdHJpZGlhIikgJT4lIHN1bW1hcml6ZShtZWFuID0gbWVkaWFuKE9UVXMpLCBtYWQgPSBtYWQoT1RVcykpCgpgYGAKR3JlYXQuIFdoYXQgYWJvdXQgdGhlIG1lZGlhbiBhbmQgbWVkaWFuIGFic29sdXRlIGRldmlhdGlvbiBmb3IgQmFjaWxsaT8gSG1tbW0uIEkgZ3Vlc3MgSSdsbCBqdXN0IGNvcHkgYW5kIHBhc3RlIHRoZSBhYm92ZSBjb2RlIGJlY2F1c2UgaXQgd2lsbCB0YWtlIDIgc2Vjb25kcy4KCmBgYHtyfQpkYXQgJT4lIGZpbHRlcihUYXhhID09ICJCYWNpbGxpIikgJT4lIHN1bW1hcml6ZShtZWFuID0gbWVkaWFuKE9UVXMpLCBtYWQgPSBtYWQoT1RVcykpCmBgYApNYXliZSBJJ2xsIGNoZWNrIG91dCBCYWN0ZXJvaWRpYSwgdG9vLi4uIGJ5IGNvcHkgYW5kIHBhc3RpbmcgQUdBSU4uCgpgYGB7cn0KZGF0ICU+JSBmaWx0ZXIoVGF4YSA9PSAiQmFjdGVyb2lkaWEiKSAlPiUgc3VtbWFyaXplKG1lYW4gPSBtZWRpYW4oT1RVcyksIG1hZCA9IG1hZChPVFVzKSkKYGBgCgpDb21lIHRvIHRoaW5rIG9mIGl0LCB0aGVyZSBhcmUgYSBsb3Qgb2YgemVyb3MgaW4gdGhpcyBkYXRhLiBNYXliZSBJIHNob3VsZCBiZSBsb29raW5nIGF0IG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBpbnN0ZWFkIG9mIG1lZGlhbiBhbmQgbWFkLiBJJ2xsIGp1c3QgZ28gYmFjayBhbmQgY2hhbmdlIHRoaXMgaW4gZWFjaCBvZiBteSAzIGxpbmVzIG9mIGNvZGUuCgoKYGBge3J9CmRhdCAlPiUgZmlsdGVyKFRheGEgPT0gIkNsb3N0cmlkaWEiKSAlPiUgc3VtbWFyaXplKG1lYW4gPSBtZWFuKE9UVXMpLCBzZCA9IHNkKE9UVXMpKQpkYXQgJT4lIGZpbHRlcihUYXhhID09ICJCYWNpbGxpIikgJT4lIHN1bW1hcml6ZShtZWFuID0gbWVhbihPVFVzKSwgc2QgPSBzZChPVFVzKSkKZGF0ICU+JSBmaWx0ZXIoVGF4YSA9PSAiQmFjdGVyb2lkaWEiKSAlPiUgc3VtbWFyaXplKG1lYW4gPSBtZWFuKE9UVXMpLCBzZCA9IHNkKE9UVXMpKQpgYGAKCgoKRG9lcyBhbnkgb2YgdGhpcyBmZWVsIGZhbWlsaWFyPz8KCkZ1bmN0aW9ucyBhbGxvdyB1cyB0bzoKCi0gYXZvaWQgcmVwZXRpdGlvbiwgbWFrZSBvdXIgY29kZSBtb3JlIGNvbXBhY3QKLSBhdm9pZCBlcnJvcnMsIGNvcHkvcGFzdGUgZXJyb3JzLCB0eXBvcwotIHZlcnNhdGlsZSwgdXBkYXRlYWJsZQotIHVwc2NhbGUsIGdlbmVyYWxpemFibGUKLSByZXN1YWJsZSwgZXZlbiBmb3Igb3RoZXIgcHJvamVjdHMhCgojI1RoZSBTdHJ1Y3R1cmUgb2YgYSBGdW5jdGlvbgoKQSBmdW5jdGlvbiBoYXMgMyBwYXJ0czogCgotIF9mb3JtYWxzXyBhcmUgdGhlIGlucHV0KHMpIG9mIHRoZSBmdW5jdGlvbiwgCi0gdGhlIF9ib2R5XyBpcyB0aGUgYnJhY2VkIGV4cHJlc3Npb24sIAotIGFuZCB0aGUgX2Vudmlyb25tZW50XyBpcyB0aGUgZW5jbG9zdXJlIGluIHdoaWNoIHRoZSBmdW5jdGlvbiB3YXMgZGVmaW5lZC4gCgpXZSBjYW4gbG9vayBhdCB0aGVzZSBmb3IgYSBmdW5jdGlvbiB3ZSBhcmUgZmFtaWxpYXIgd2l0aCwgYHJlYWQuY3N2YC4gRWFjaCBhcmd1bWVudCBpcyBzcGVjaWZpZWQgd2l0aCBgJGAgYW5kIGFueSBkZWZhdWx0IHZhbHVlcyBhcmUgbGlzdGVkIGBbMV1gLiBUbyB1c2UgYHJlYWQuY3N2YCB5b3UgbXVzdCBpbnB1dCBhIGZpbGUgLSB0aGlzIGFyZ3VtZW50IGhhcyBubyBkZWZhdWx0IHZhbHVlLiBUaGUgcmVzdCBvZiB0aGUgYXJndW1lbnRzIGhhdmUgYSBkZWZhdWx0IHZhbHVlIChzdWNoIGFzIGBoZWFkZXIgPSBUUlVFYCksIGJ1dCBjYW4gYmUgbW9kaWZpZWQgYnkgc3BlY2lmeWluZyB0aGUgYXJndW1lbnQgKGJ5IGlucHV0dGluZyBgaGVhZGVyID0gRkFMU0VgKS4gTW9zdCBmdW5jdGlvbiBkZWZhdWx0cyBhcyAnc2Vuc2libGUnLCBidXQgaWYgeW91IGhhdmUgbm90IHVzZWQgdGhlIGZ1bmN0aW9uIGJlZm9yZSBpdCBpcyB3b3J0aCByZWFkaW5nIGFib3V0IHRoZSBhcmd1bWVudHMgaW4gdGhlIGhlbHAgbWVudS4gRVNQRUNJQUxMWSBkbyB0aGlzIHdpdGggbWF0aGVtYXRpY2FsIGZ1bmN0aW9ucyB0byBzZWUgd2hhdCBhc3N1bXB0aW9ucyB5b3UgYXJlIG1ha2luZyBieSB1c2luZyB0aGUgZGVmYXVsdCBwYXJhbWV0ZXJzLiBgcmVhZC5jc3ZgIGFsc28gaGFzIHRoZSBtYWdpY2FsIGAuLi5gIGFyZ3VtZW50LiBUaGlzIGFyZ3VtZW50IGFsbG93cyB5b3UgdG8gcGFzcyBhcmJpdHJhcnkgYXJndW1lbnRzIHRvIGFub3RoZXIgZnVuY3Rpb24uIFdlIHdpbGwgc2VlIHRoaXMgaW4gdXNlIHNob3J0bHkuCgoKYGBge3J9CmZvcm1hbHMocmVhZC5jc3YpCmBgYAoKVGhlIGJvZHkgb2YgYHJlYWQuY3N2YCBsb29rcyBzdXNwaWNpb3VzbHkgbGlrZSB0aGUgZnVuY3Rpb24gYHJlYWQudGFibGVgIGJlY2F1c2UgaXQgaXMsIGluZGVlZCBhICd3cmFwcGVyJyBmdW5jdGlvbiB3aGljaCB1c2VzIGFuIGV4aXN0aW5nIGZ1bmN0aW9uIHdpdGggZGlmZmVyZW50IHZhbHVlcyBmb3IgZGVmYXVsdCBhcmd1bWVudHMuCgpgYGB7cn0KYm9keShyZWFkLmNzdikKYGBgClRoZSBlbnZpcm9ubWVudCBmb3IgYHJlYWQuY3N2YCBpcyB0aGUgdXRpbHMgcGFja2FnZS4gV2hlbiB3ZSBtYWtlIGEgZnVuY3Rpb24sIGl0IHdpbGwgYmUgY3JlYXRlZCBpbiAnLkdsb2JhbEVudicgdGhlICdnbG9iYWwgZW52aXJvbm1lbnQnLiBUaGUgZ2xvYmFsIGVudmlyb25tZW50IGlzIG91ciBpbnRlcmFjdGl2ZSB3b3Jrc3BhY2UsIHZpZXdlZCBpbiB0aGUgZW52aXJvbm1lbnQgd2luZG93IGluIHRoZSB0b3AgcmlnaHQsIGFuZCBjYW4gYmUgdGhvdWdodCBvZiBhcyBhIGJhZyB0aGF0IGhvbGRzIGFsbCBvZiBvdXIgdmFyaWFibGVzIGFuZCBmdW5jdGlvbnMuIE5vdGUgdGhhdCB3ZSBjYW4gdXNlIHRoZSBkcm9wZG93biBtZW51IHRvIGNoYW5nZSB0aGUgZW52aXJvbm1lbnQgdG8gcGFja2FnZTp1dGlscywgYW5kIHdlIGNhbiBmaW5kIGByZWFkLmNzdmAgaW4gdGhhdCBlbnZpcm9ubWVudC4KIApgYGB7cn0KZW52aXJvbm1lbnQocmVhZC5jc3YpCmBgYAoKIyNXcml0aW5nIEZ1bmN0aW9ucwoKClRoZSBiYXNpYyBmb3JtIG9mIGEgZnVuY3Rpb24gdGFrZXMgaW4gYXJndW1lbnRzIChzcGVjaWZpY2F0aW9ucyBieSB0aGUgdXNlciksIGhhcyBzdGF0ZW1lbnRzIG9yIGV4cHJlc3Npb25zICh3aGF0IHRoZSBmdW5jdGlvbiBkb2VzKSwgYW5kIHJldHVybnMgYW4gb2JqZWN0LiBBIGZ1bmN0aW9uIGRvZXMgbm90IG5lZWQgdG8gYmUgbmFtZWQgdG8gYmUgdXNlZCwgaG93ZXZlciwgdGhlIGdyZWF0ZXN0IHV0aWxpdHkgY29tZXMgZnJvbSByZXVzaW5nIGZ1bmN0aW9ucy4gQSBuYW1lZCBmdW5jdGlvbiBpcyBjYWxsZWQgdXNpbmcgcGFyZW50aGVzZXM6IGBuYW1lZF9mdW5jdGlvbihhcmd1bWVudHMpYC4KCkRpZmZlcmVudCBmcm9tIG90aGVyIHByb2dyYW1taW5nIGxhbmd1YWdlcywgUiBpcyBub3QgcGlja3kgYWJvdXQgd2hpdGVzcGFjZSAodGhlIGFycmFuZ2VtZW50IG9mIHRhYnMsIHNwYWNlcywgaW5kZW50cykuIEhvd2V2ZXIgZm9ybWF0dGluZyB5b3VyIGZ1bmN0aW9uIGFzIGJlbG93IChzdGF0ZW1lbnRzIGluZGVudGVkIGJlbG93IHlvdXIgYXJndW1lbnRzKSB3aWxsIGhlbHAgdG8gbWFrZSBpdCByZWFkYWJsZSBhbmQgZmFtaWxpYXIgdG8gb3RoZXIgbGFuZ3VhZ2VzLiAKClRoaXMgbWF5IGJlIGEgZ29vZCB0aW1lIHRvIHBvaW50IG91dCB0aGF0IHBsYWNpbmcgeW91ciBjdXJzb3IgYmVzaWRlIGEgY2xvc2luZyBicmFja2V0IHdpbGwgaGlnaGxpZ2h0IGl0cyBjb3JyZXNwb25kaW5nIG9wZW5pbmcgYnJhY2tldC4gVGhpcyBpcyByZWFsbHkgdXNlZnVsIGFzIHlvdXIgY29kZSBnZXRzIG1vcmUgY29tcGxleC4gQWxzbywgdHJ5IGRlbGV0aW5nIHRoZSBjbG9zaW5nIGJyYWNrZXQgYmVzaWRlICdvYmplY3QnLiBZb3Ugd2lsbCBzZWUgdGhhdCBSIGlzIGdpdmluZyB5b3UgYSBzZXJpZXMgb2Ygd2FybmluZ3MgKGJyYWNrZXRzIGhpZ2hsaWdodGVkIGluIHJlZCwgYW5kICd4JyBiZXNpZGUgMyBsaW5lcyBvZiBjb2RlKS4gQWdhaW4sIHRoaXMgaXMgYSBoZWxwZnVsIHdhcm5pbmcgdGhhdCBzYXZlcyB5b3UgdGltZSBmcm9tIHJ1bm5pbmcgYW4gaW5jb21wbGV0ZSBmdW5jdGlvbi4KCmBgYHtyfQpuYW1lZF9mdW5jdGlvbiA8LSAKICBmdW5jdGlvbihhcmd1bWVudHMpIHsKICAgICAgc3RhdG1lbnRzCiAgICAgIHJldHVybihvYmplY3QpIAogIH0KYGBgCgoKV2Ugd2FudCB0byB3cml0ZSBhIGZ1bmN0aW9uIHRoYXQgd2lsbCBjYWxjdWxhdGUgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBmb3IgYSBnaXZlbiBUYXhhLiAKCldlIGNhbiBzdGFydCBieSBuYW1pbmcgb3VyIGZ1bmN0aW9uIHNvbWV0aGluZyB2YWd1ZWx5IGluZm9ybWF0aXZlLiAoVGVjaG5pY2FsbHksIHlvdSBjYW4gbmFtZSB5b3VyIGZ1bmN0aW9ucyBhbmQgdmFyaWFibGVzIHdoYXRldmVyIHlvdSB3YW50LiBJIGNvdWxkIG5hbWUgdGhlbSBhZnRlciBBdmVuZ2VycyBjaGFyYWN0ZXJzLCBidXQgcGVvcGxlIChvciBteXNlbGYgaW4gMyBtb250aHMpIHdvdWxkIGhhdmUgdG8gd29yayBwcmV0dHkgaGFyZCB0byBmaWd1cmUgb3V0IHdoYXQgSSB3YXMgZG9pbmcuIFJlbWVtYmVyLCB3aXRoIGdyZWF0IHBvd2VyIGNvbWVzIGdyZWF0IHJlc3BvbnNpYmlsaXR5KS4gCgpPdXIgZnVuY3Rpb24gaXMgZ29pbmcgdG8gdGFrZSAnVGF4YScgYXMgYW4gYXJndW1lbnQuIEZvciBzb21lIG9mIHlvdSwgd2hlbiB5b3UgdHlwZSAneycgUiB3aWxsIGF1dG9tYXRpY2FsbHkgZ2l2ZSB5b3UgeW91ciBjbG9zaW5nIGJyYWNrZXQgJ30nIHNvIHlvdSB3b24ndCBmb3JnZXQgLSBldmVyIHRyeWluZyB0byBiZSBoZWxwZnVsLCB0aGFua3MgUi4gTmV4dCB3ZSBjYW4gdGFrZSBvdXIgcHJldmlvdXMgY29kZSwgYW5kIHNhdmUgdGhlIG91dHB1dCBpbnRvIGFuIG9iamVjdCBjYWxsZWQgJ3N1bW1hcnlfZGF0Jy4gVGhpcyBpcyBiZWNhdXNlIEkgZG9uJ3Qgd2FudCB0byBvdmVyd3JpdGUgbXkgb3JpZ2luYWwgZGF0YSBmcmFtZSBldmVyeSB0aW1lIEkgcnVuIHRoZSBmdW5jdGlvbi4gV2UgcmVwbGFjZSBvdXIgc3BlY2lmaWMgVGF4YSwgJ0Nsb3N0cmlkaWEnLCB3aXRoIG91ciBhcmd1bWVudCwgJ1RheGEnLiBUaGlzIGlzIG91ciBleHByZXNzaW9uLiBBbGwgd2UgaGF2ZSB0byBkbyBub3cgaXMgYHJldHVybmAgb3VyIGRhdGEgZnJhbWUgYW5kIG1ha2Ugc3VyZSBhbGwgb2Ygb3VyIGJyYWNrZXRzIG1hdGNoLgoKVm9pbGEhIAoKYGBge3J9Cm1lYW5fZnVuYyA8LSAKICBmdW5jdGlvbihUYXhhKSB7CiAgICBzdW1tYXJ5X2RhdCA8LSBmaWx0ZXIoZGF0LCBUYXhhID09IFRheGEpICU+JSAKICAgIHN1bW1hcml6ZShtZWFuID0gbWVhbihPVFVzKSwgc2QgPSBzZChPVFVzKSkKICAgIHJldHVybihzdW1tYXJ5X2RhdCkKfQpgYGAKCldoZW4geW91IHJ1biB0aGlzIGZ1bmN0aW9uLCBpdCB3aWxsIGFwcGVhciwgbmFtZWQgYW5kIHdpdGggaXRzIGFyZ3VtZW50cywgaW4gdGhlIGdsb2JhbCBlbnZpcm9ubWVudC4gSWYgeW91IGNsaWNrIG9uIHRoZSB3aGl0ZSBib3ggdG8gdGhlIHJpZ2h0IG9mIHRoZSBmdW5jdGlvbiwgeW91IGNhbiBzZWUgdGhlIGNvZGUgZm9yIHlvdXIgZnVuY3Rpb24uIFlvdSBjYW4gbm93IHVzZSB5b3VyIGZ1bmN0aW9uLiAKCmBgYHtyfQptZWFuX2Z1bmMoIkNsb3N0cmlkaWEiKQptZWFuX2Z1bmMoIkJhY2lsbGkiKQptZWFuX2Z1bmMoIkJhY3RlcmlvZGlhIikKYGBgCmBgYHtyfQpvdXQgPC0gbWVhbl9mdW5jKCJCYWN0ZXJpb2RpYSIpCmBgYAoKCmBgYHtyfQptZWFuX2Z1bmMgPC0gCiAgZnVuY3Rpb24oVGF4YSkgewogICAgZmlsdGVyKGRhdCwgVGF4YSA9PSBUYXhhKSAlPiUgCiAgICBzdW1tYXJpemUobWVhbiA9IG1lYW4oT1RVcyksIHNkID0gc2QoT1RVcykpCiAgICBwcmludChwYXN0ZSgnVGhpcyBpcyB0aGUgbWVhbiBmb3InLCBUYXhhKSkKfQpgYGAKCiMjVW5kZXJzdGFuZGluZyBMZXhpY2FsIFNjb3BpbmcgCgotIHdoeSBkb2VzIGRhdCBnZXQgY2hhbmdlZCwgYnV0IHN1bW1hcnlfZGF0IGRvZXMgbm90IGVuZCB1cCBiZWluZyBhIGdsb2JhbCBlbnZpcm9ubWVudGFsIHZhcmlhYmxlIHdoZW4gcmV0dXJuZWQ/CgoKLW1heSB3YW50IHRvIGJlIGFibGUgdG8gdGFrZSBpbnRvIGRpZmZlcmVudCBkYXRhIGZyYW1lcy4uLgoKCmBgYHtyfQptZWFuX2Z1bmMgPC0gZnVuY3Rpb24oZGF0LCB4KSB7CiAgZGF0IDwtIGZpbHRlcihkYXQsIFRheGEgPT0geCkgJT4lIAogICAgc3VtbWFyaXplKG1lYW4gPSBtZWFuKE9UVXMpLCBzZCA9IHNkKE9UVXMpKQp9CgptZWFuX2Z1bmMoZGF0LCAiQ2xvc3RyaWRpYSIpCmBgYAoKCgoKQWxsb3dzIHVzIHRvIHZhcnkgdGhlIFRheGEgYW5kIHRoZSBmdW5jdGlvbnMuCgpgYGB7cn0KbWVhbl9mdW5jIDwtIGZ1bmN0aW9uKHgsIGZ1bnMgPSBsaXN0KG1lYW4gPSBtZWFuLCBzZCA9IHNkKSkgewogIGRhdCA8LSBmaWx0ZXIoZGF0LCBUYXhhID09IHgpIAogIGxhcHBseShmdW5zLCBmdW5jdGlvbihmKSBmKGRhdCRPVFVzKSkKfQoKbWVhbl9mdW5jKCJDbG9zdHJpZGlhIiwgZnVucyA9IGxpc3QobWVhbiA9IG1lYW4sIG1lZGlhbiA9IG1lZGlhbiwgc2QgPSBzZCkpCgoKYGBgCgojI0RlZmVuc2l2ZSBQcm9ncmFtbWluZwoKCiMjI1Rlc3RpbmcgQXJndW1lbnRzIGFuZCBWYWxpZGl0eSBvZiB5b3VyIEZ1bmN0aW9uCgpZb3UgbWlnaHQgdGhpbmsgdGhhdCB5b3UgYXJlIGRvbmUgd2l0aCB0aGlzIGZ1bmN0aW9uLiBZb3UgaGF2ZSB0aGUgYW5zd2VycywgcmlnaHQ/IEJ1dCBmdW5jdGlvbnMgYXJlIG5vdCBmb29scHJvb2YuCgpgYGB7cn0KbWVhbl9mdW5jKCJDbG9zdHJpZGlhIiwgZnVucyA9IGxpc3QobWVhbiA9IHF1YW50aWxlLCBtZWRpYW4gPSBtZWRpYW4sIHNkID0gbWVhbikpCmBgYAoKSG1tbS4uLiBtaXNsYWJlbGxlZCBudW1iZXJzIGNvdWxkIGdldCBhIGxpdHRsZSBjb25mdXNpbmcuIAoKV2hhdCBpZiBJIHdhbnRlZCB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciAzIFRheGEgYXQgb25jZT8KCmBgYHtyfQptZWFuX2Z1bmMoYygiQ2xvc3RyaWRpYSIsICJCYWN0ZXJpb2RpYSIsICJCYWNpbGxpIikpCmBgYApVaCBvaC4gVGhhdCdzIGJhZC4gSSBkaWRuJ3Qgd2FudCB0byBtZXJnZSB0aGUgMyBUYXhhLi4uIG9yIGRpZCBpdCB0YWtlIHRoZSBsYXN0IHZhbHVlPwoKWW91ciBuZXh0IHN0ZXAgaXMgdG8gdHJ5IGFuZCBpbnRlbnRpb25hbGx5IGJyZWFrIHlvdXIgZnVuY3Rpb24uIEdpdmUgaXQgaW5wdXQgdGhhdCB3YXNuJ3QgbWVhbnQgZm9yIGl0IGFuZCBzZWUgd2hhdCBoYXBwZW5zLiBUcm91YmxlLXNob290aW5nIHRoaXMgd2F5IGlzIGNhbGxlZCAnZGVmZW5zaXZlIHByb2dyYW1taW5nJy4gV2hhdCB3ZSBhcmUgZ29pbmcgdG8gZG8gaXMgaW1wbGVtZW50IGNoZWNrcyBhbmQgYmFsYW5jZXMgc3VjaCB0aGF0IG91ciBmdW5jdGlvbiBjYW4ndCBkbyBhbnl0aGluZyB3ZWlyZC4gCgpJbml0aWFsIGNoZWNrcyBjYW4gYmUgdGhpbmdzIGxpa2UgLSB3aGF0IGhhcHBlbnMgd2hlbiBJIGdpdmUgYW4gaW5wdXQgdGhhdCBpcyBudW1lcmljIHdoZW4gSSBhbSBleHBlY3RpbmcgY2hhcmFjdGVyIGRhdGE/IFdoYXQgaGFwcGVucyBpZiB0aGVyZSBpcyBhbiBOQSBpbiB0aGUgZGF0YXNldD8gV2hhdCBoYXBwZW5zIGlmIHRoZXJlIGlzIGEgdHlwbz8gV2hhdCBoYXBwZW5zIGlmIEkgYW0gZ2l2ZW4gYW4gdW5leHBlY3RlZCBkYXRhIHN0cnVjdHVyZT8gV2hhdCBoYXBwZW5zIGlmIHRoZXJlIGlzIG1vcmUgb3IgbGVzcyBkYXRhIHRoYW4gSSBhbSBleHBlY3Rpbmc/IFdoYXQgaGFwcGVucyB3aGVuIEkgaW5wdXQgbG9naWNhbCBkYXRhPwoKYGBge3J9Cm1lYW5fZnVuYygiQmFjaWxsbGxpIikKYGBgCmBgYHtyfQptZWFuX2Z1bmMoTkEpCmBgYApgYGB7cn0KbWVhbl9mdW5jKDIxMCkKYGBgCmBgYHtyfQptZWFuX2Z1bmMoKQpgYGAKCiMjI0Vycm9yIEhhbmRsaW5nCgoKIyMjVW5pdCBUZXN0cwoKLXRlc3R0aGF0IGFzc3VtcHRpb24gZm9yIGZ1bmN0aW9uIG5hbWUgPSBuYW1lPwoKUmV0dXJucyB0aGUgbGFzdCBsaW5lLi4uIHVubGVzcyBleHBsaWNpdGx5IGNhbGxlZCB3aXRoIGByZXR1cm5gCgpgYGB7cn0KbWVhbl9mdW5jIDwtIGZ1bmN0aW9uKHgsIGZ1bnMgPSBsaXN0KG1lYW4gPSBtZWFuLCBzZCA9IHNkKSkgewogIGRhdCA8LSBmaWx0ZXIoZGF0LCBUYXhhID09IHgpIAogIGxhcHBseShmdW5zLCBmdW5jdGlvbihmKSBmKGRhdCRPVFVzKSkKICByZXR1cm4oZGF0KQp9CmBgYAoKCgoKCgoKCgoKCgpnZW5lcmFsaXplIHRvIGNvdW50cnksIGJhY3RlcmlhLCAjb3R1cyBhYm92ZS9iZWxvdyBtZWFuPyByYW5raW5nIHBvc2l0aW9uPwoKCgpfQmFzaWMgU3ludGF4IG9mIEZ1bmN0aW9ucyBpbiBSXwoKLSB0aGUgc3RydWN0dXJlIG9mIGEgZnVuY3Rpb24gKGZvcm1hbHMsIGJvZHksIHBhcmVudCBlbnZpcm9ubWVudCkKLSB1bmRlcnN0YW5kaW5nIHNjb3BpbmcKLSBuYW1pbmcgY29udmVudGlvbnMKLSBnZW5lcmFsaXppbmcgYSBmdW5jdGlvbgotIHNldHRpbmcgZGVmYXVsdCB2YWx1ZXMKLSBhZGRpbmcgJy4uLicKLSB0aGUgb3V0cHV0IG9mIHlvdXIgZnVuY3Rpb24KLSBzb3VyY2luZyBmdW5jdGlvbnMKLSBOQXMgaW4gZnVuY3Rpb25zCi0gcmV0dXJuCiAgCl9IYW5kbGluZyBFcnJvcnNfCgotIHN0b3BpZm5vdCgpLCB3YXJuaW5nKCksIHN1cHByZXNzV2FybmluZ3MoKSwgdHJ5Q2F0Y2goKQotIGlmIHN0b3AKCl9UZXN0aW5nIEFyZ3VtZW50cyBhbmQgVmFsaWRpdHkgb2YgeW91ciBGdW5jdGlvbl8KCi0gaW5mb3JtYWwgdGVzdGluZwotIHRlc3R0aGF0KCkgZm9yIGZvcm1hbCB1bml0IHRlc3RpbmcKLSBhc3NlcnR0aGF0CgpfVXBzY2FsaW5nIHdpdGggZnVuY3Rpb25zXwoKZnVuY3Rpb25zIGNhbiBiZToKCi0gYXNzaWduZWQgaW4gdmFyaWFibGVzCi0gc3RvcmVkIGluIGxpc3RzCi0gcGFzc2VkIGFzIGFyZ3VtZW50cyB0byBvdGhlciBmdW5jdGlvbnMKLSBjcmVhdGVkIGluc2lkZSBvdGhlciBmdW5jdGlvbnMKLSBvdXRwdXQgYnkgb3RoZXIgZnVuY3Rpb25zCgpEUlkgcHJpbmNpcGxlIC0gRG9uJ3QgUmVwZWF0IFlvdXJzZWxmCgoKCgoqKioKX19DaGFsbGVuZ2VfXyAKCgo8ZGl2IHN0eWxlPSJmbG9hdDpsZWZ0O21hcmdpbjowIDEwcHggMTBweCAwIiBtYXJrZG93bj0iMSI+CiFbXShpbWcvUGVFZUs3SC5naWYpe3dpZHRoPTE1MHB4fQoKPC9kaXY+CgpXcml0ZSBhIGZ1bmN0aW9uIHRvIGNoZWNrIHRoYXQgdGhlIHBhY2thZ2VzIGMoImRwbHlyIiwgInJlYWR4bWwiLCAidGlkeXIiKSBhcmUgaW5zdGFsbGVkIGFuZCB0byBsb2FkIHRoZSBwYWNrYWdlcy4gVGhlIGZ1bmN0aW9uIHNob3VsZCBpbnN0YWxsIHRoZSBwYWNrYWdlcyBpZiB0aGV5IGFyZSBub3QgYWxyZWFkeSBpbnN0YWxsbGVkLiBXcml0ZSBhIHdhcm5pbmcgaWYgdGhlIHBhY2thZ2UgaXMgbm90IGluc3RhbGxlZCwgYnV0IGlzIGJlaW5nIGluc3RhbGxlZC4gV2lsbCB0aGlzIGZ1bmN0aW9uIHdvcmsgZm9yIG90aGVyIHBhY2thZ2VzPwoKCjwvYnI+CjwvYnI+CjwvYnI+CgoqKioKCgoqKioKX19DaGFsbGVuZ2VfXyAKCgo8ZGl2IHN0eWxlPSJmbG9hdDpsZWZ0O21hcmdpbjowIDEwcHggMTBweCAwIiBtYXJrZG93bj0iMSI+CiFbXShpbWcvZ3VpbmVhLXBpZy1zdHVmZmluZy1oaXMtZmFjZS13aXRoLWNhcnJvdC0yNTMxLmpwZyl7d2lkdGg9MTUwcHh9Cgo8L2Rpdj4KCldyaXRlIGEgZnVuY3Rpb24gdG8gcmVhZCBhbGwgLmNzdiBmaWxlcyBpbiBhIGRpcmVjdG9yeSBpbnRvIFIgYW5kIHNhdmUgZWFjaCBvZiB0aGVtIGluIGEgc2VwYXJhdGUgZGF0YSBmcmFtZS4KCgo8L2JyPgo8L2JyPgo8L2JyPgoKKioqCgoKCl9fUmVzb3VyY2VzOl9fIAoKPGh0dHA6Ly9zdGF0NTQ1LmNvbS9ibG9jazAxMV93cml0ZS15b3VyLW93bi1mdW5jdGlvbi0wMS5odG1sPiAgICAgCjxodHRwOi8vc3RhdDU0NS5jb20vYmxvY2swMTFfd3JpdGUteW91ci1vd24tZnVuY3Rpb24tMDIuaHRtbD4gICAgIAo8aHR0cDovL3N0YXQ1NDUuY29tL2Jsb2NrMDExX3dyaXRlLXlvdXItb3duLWZ1bmN0aW9uLTAzLmh0bWw+ICAgICAKPGh0dHA6Ly9tYXphbWFzY2llbmNlLmNvbS9Xb3JraW5nV2l0aERhdGEvP3A9OTEyPgo8aHR0cDovL2Fkdi1yLmhhZC5jby5uei9GdW5jdGlvbnMuaHRtbD4KCgojUG9zdC1MZXNzb24gQXNzZXNzbWVudAoqKioKCllvdXIgZmVlZGJhY2sgaXMgZXNzZW50aWFsIHRvIGhlbHAgdGhlIG5leHQgY29ob3J0IG9mIHRyYWluZWVzLiBQbGVhc2UgdGFrZSBhIG1pbnV0ZSB0byBjb21wbGV0ZSB0aGUgZm9sbG93aW5nIHNob3J0IHN1cnZleToKaHR0cHM6Ly93d3cuc3VydmV5bW9ua2V5LmNvbS9yL1g1QzJKR1IKCjwvYnI+CgoqKioKCjwvYnI+CgpUaGFua3MgZm9yIGNvbWluZyEhIQoKIVtdKGltZy9yc3R1ZGlvLWJvbWIucG5nKXt3aWR0aD0zMDBweH0=